package com.small.nacos.naming.core;

import org.apache.commons.collections4.CollectionUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author zhoujin
 * @Date 2022/10/13 19:28
 */
public class Cluster extends com.alibaba.nacos.api.naming.pojo.Cluster {

    private Set<Instance> persistentInstances = new HashSet<>();

    private Set<Instance> ephemeralInstances = new HashSet<>();

    private Service service;

    public Cluster(String clusterName, Service service) {
        super(clusterName);
        this.service = service;
    }

    public List<Instance> allIPs() {
        List<Instance> allInstances = new ArrayList<>();
        allInstances.addAll(persistentInstances);
        allInstances.addAll(ephemeralInstances);
        return allInstances;
    }

    public List<Instance> getIPs(boolean ephemeral) {
        List<Instance> instances = new ArrayList<>();
        Set<Instance> toUpdateInstances;
        if (ephemeral) {
            toUpdateInstances = ephemeralInstances;
        } else {
            toUpdateInstances = persistentInstances;
        }
        instances.addAll(toUpdateInstances);
        return instances;
    }

    public void updateIps(List<Instance> ips, boolean ephemeral) {

        Set<Instance> toUpdateInstances = ephemeral ? ephemeralInstances : persistentInstances;

        //1.更改了的实例
        HashMap<String, Instance> oldIpMap = new HashMap<>(toUpdateInstances.size());

        for (Instance ip : toUpdateInstances) {
            oldIpMap.put(ip.getDatumKey(), ip);
        }
        //旧实例中有但是已经改变的实例
        List<Instance> updatedIPs = updatedIps(ips, oldIpMap.values());
        if (updatedIPs.size() > 0) {
            for (Instance ip : updatedIPs) {
                Instance oldIP = oldIpMap.get(ip.getDatumKey());
                //对应已更改的实例健康状态不做更新
                ip.setHealthy(oldIP.isHealthy());
            }
        }

        toUpdateInstances = new HashSet<>(ips);
        if (ephemeral) {
            ephemeralInstances = toUpdateInstances;
        } else {
            persistentInstances = toUpdateInstances;
        }
    }

    private List<Instance> updatedIps(Collection<Instance> newInstance, Collection<Instance> oldInstance) {
        // 获取newInstance和oldInstance的交集
        // 判断instance是否相等采用的是equal方法，其实只是比较的ip、port 和 ephemeral
        // getIp().equals(other.getIp()) && (getPort() == other.getPort() || getPort() == 0) && this.isEphemeral() == other.isEphemeral();
        // 如果还有其它的元素不相等，其实也判断的相等，会有交集，比如weight，healthy不相等一样会判断为相等
        List<Instance> intersects = (List<Instance>) CollectionUtils.intersection(newInstance, oldInstance);
        Map<String, Instance> stringIpAddressMap = new ConcurrentHashMap<>(intersects.size());

        for (Instance instance : intersects) {
            stringIpAddressMap.put(instance.getIp() + ":" + instance.getPort(), instance);
        }

        Map<String, Integer> intersectMap = new ConcurrentHashMap<>(newInstance.size() + oldInstance.size());
        Map<String, Instance> updatedInstancesMap = new ConcurrentHashMap<>(newInstance.size());
        Map<String, Instance> newInstancesMap = new ConcurrentHashMap<>(newInstance.size());

        for (Instance instance : oldInstance) {
            if (stringIpAddressMap.containsKey(instance.getIp() + ":" + instance.getPort())) {
                //新集合中有的，旧集合也有的实例放进去
                intersectMap.put(instance.toString(), 1);
            }
        }

        for (Instance instance : newInstance) {
            //交集中有
            if (stringIpAddressMap.containsKey(instance.getIp() + ":" + instance.getPort())) {
                //旧集合中有的，toString一样
                if (intersectMap.containsKey(instance.toString())) {
                    intersectMap.put(instance.toString(), 2);
                    //旧集合中有的，toString不一样
                } else {
                    intersectMap.put(instance.toString(), 1);
                }
            }
            //旧集合中没有的实例
            newInstancesMap.put(instance.toString(), instance);

        }

        for (Map.Entry<String, Integer> entry : intersectMap.entrySet()) {
            String key = entry.getKey();
            Integer value = entry.getValue();

            if (value == 1) {
                if (newInstancesMap.containsKey(key)) {
                    updatedInstancesMap.put(key, newInstancesMap.get(key));
                }
            }
        }

        return new ArrayList<>(updatedInstancesMap.values());
    }

    public List<Instance> allIPs(boolean ephemeral) {
        return ephemeral ? new ArrayList<>(ephemeralInstances) : new ArrayList<>(persistentInstances);
    }

    public Set<Instance> getPersistentInstances() {
        return persistentInstances;
    }

    public void setPersistentInstances(Set<Instance> persistentInstances) {
        this.persistentInstances = persistentInstances;
    }

    public Service getService() {
        return service;
    }

    public void setService(Service service) {
        this.service = service;
    }

    public Set<Instance> getEphemeralInstances() {
        return ephemeralInstances;
    }

    public void setEphemeralInstances(Set<Instance> ephemeralInstances) {
        this.ephemeralInstances = ephemeralInstances;
    }
}
